/*
** Two types of polytopes are implemented.
** One is 2D polygon. The other is 3D polyhedron
** Polytope is the underlying geometric structure of tiles/stencil/compenent
*/

#ifndef _POLYTOPE_H_
#define _POLYTOPE_H_


#ifdef WIN32
#include <Windows.h>
#endif
#include <GL/GL.h>

#include <string>
#include <cmath>
#include <sstream>

#include <boost/math/constants/constants.hpp>
//CGAL include
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polygon_2.h>
#include <CGAL/Aff_transformation_2.h>
#include <CGAL/Iso_rectangle_2.h>
#include <CGAL/centroid.h>
#include <CGAL/partition_2.h>
#include <CGAL/bounding_box.h>

namespace Nesting
{
	/* polytope as base class */
	class Nesting_polytope
	{

	public:
		
		// query
		virtual unsigned int dimension() const = 0;
		// unsigned volume
		virtual double volume() const = 0; 

		virtual void gl_draw(const GLfloat *fill_color) const = 0;
		virtual void redraw(const GLfloat *fill_color) const = 0;
		virtual void gl_draw_bbox() const = 0; // draw bounding box

		virtual ~Nesting_polytope() { }

	};

	typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
	typedef CGAL::Polygon_2<K> CGAL_polygon;

	class Nesting_polygon : public Nesting_polytope, public CGAL_polygon
	{

		typedef CGAL::Iso_rectangle_2<K> Iso_rectangle_2;

	public:

		typedef CGAL_polygon::Point_2 Point;

	public:
		//Nesting_polygon(const std::string& poly_fm_filename);
		template <class ForIter> Nesting_polygon(ForIter first, ForIter last) : CGAL_polygon(first, last) { }
		Nesting_polygon() : CGAL_polygon() { }
		~Nesting_polygon();

		unsigned int dimension() const { return 2; }

		double volume() const { return std::fabs(CGAL_polygon::area()); }

		void gl_draw(const GLfloat *fill_color) const;

		void gl_draw_bbox() const;

		void redraw(const GLfloat *fill_color) const;

		void translate(double dx, double dy);

		void rotate(double angle) ;

		void self_rotate(double angle);

		Iso_rectangle_2 bbox() const;

	private:
		mutable bool update_display_list;
		mutable GLuint display_list_id;
	};

	// input function template for Nesting_polygon set from files
	template <class InsertIter>
	InsertIter read_polygon_set(const std::string& poly_fm_filename, InsertIter res, const std::string fmt = "poly")
	{
		std::ifstream fin(poly_fm_filename.c_str());
		std::string error_info("Bad input file format :" + poly_fm_filename);
		if ( !fin.is_open() )
			throw Bad_input("Polygon file cannot be openned.\n");
		std::cout<<"Open file: "<<poly_fm_filename<<" as polygon input file..."<<std::endl;
		// backup for memory safety
		typename InsertIter::container_type okay_elements;	
		
		try
		{
			InsertIter backup_iter(okay_elements);
			if (fmt == "poly")
			{
				// gross number of polygons
				unsigned int nb_polygons;
				if (!(fin>>nb_polygons))	throw Bad_input(error_info);

				for (unsigned int i = 0; i < nb_polygons; i++)
				{
					unsigned int nb_vtx;
					if ( !(fin >> nb_vtx) )	throw Bad_input(error_info);
					Nesting_polygon *np = new Nesting_polygon;
					*backup_iter = *res = np;
					for (unsigned int j = 0; j < nb_vtx; j++)
					{
						double x, y;
						if ( !(fin >> x) )	throw Bad_input(error_info);
						if ( !(fin >> y) )	throw Bad_input(error_info);
						np->push_back(Nesting_polygon::Point(x,y));
					}
					++res;
					++backup_iter;
				}
			}
			else if (fmt == "txt") // lack of care on exception detection and handling
			{
				while (fin)
				{
					std::string token;
					std::istringstream iss;

					if (!(fin >> token)) break;
					while (token != "PIECE")	
						if ( !(fin >> token) ) // end of file
							break; 

					std::getline(fin, token); // "piece 1"
					std::getline(fin, token); // "quantity"
					unsigned int nb_polygons, nb_vtx;

					//if ( !(fin>>nb_polygons) )	throw Bad_input(error_info);
					std::getline(fin, token);
					iss.str(token);
					iss >> nb_polygons;

					std::getline(fin, token);	// "number of vertices"

					//if ( !(fin>>nb_vtx) )		throw Bad_input(error_info);
					std::getline(fin, token);
					iss.str(token);
					iss.seekg(std::ios_base::beg);
					iss >> nb_vtx;

					std::getline(fin, token); // "vertices (x, y)"
					Nesting_polygon **np = new Nesting_polygon*[nb_polygons];
					for (unsigned int i = 0; i < nb_polygons; i++)
						np[i] = new Nesting_polygon;
					
					for (unsigned int i = 0; i < nb_vtx; i++)
					{
						double x, y;
						if ( !(fin >> x) )	throw Bad_input(error_info);
						if ( !(fin >> y) )	throw Bad_input(error_info);
						for (unsigned int j = 0; j < nb_polygons; j++)
							np[j]->push_back(Nesting_polygon::Point(x,y));
					}
					for (unsigned int i = 0; i < nb_polygons; i++, ++res, ++backup_iter)
						*backup_iter = *res = np[i];
				}
			}
			
		}
		catch (Bad_input&)
		{
			// release allocated elements
			typename InsertIter::container_type::iterator it;
			for (it = okay_elements.begin(); it != okay_elements.end(); ++it)
				delete *it;
			throw; //rethrow for external handle
		}
		std::cout<<"Finished reading polygons...\n";
		return res;
	}
}

#endif